using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using System.ComponentModel;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Geodatabase;
using BesAsm.Emgaats.Core;
using BesAsm.Framework.Reporting;
using BesAsm.ModelData.Gis;
using BesAsm.Framework.CommonControls;
using BesAsm.Emgaats.Core.Commands;

namespace BesAsm.Emgaats.Core
{
  /// <summary>
  /// The primary Emgaats ArcGIS extension
  /// </summary>  
  [ClassInterface(ClassInterfaceType.None)]
  public class EmgaatsCoreExtension : ESRI.ArcGIS.Desktop.AddIns.Extension
  {
    private static EmgaatsCoreExtension _emgaatsExtension;

    private Mediator _mediator;
    private LayerLibrarian _librarian;

    public EmgaatsCoreExtension()
    {
      _emgaatsExtension = this;
    }

    internal static EmgaatsCoreExtension GetExtension()
    {
      if (_emgaatsExtension == null)
      {
        UID uid = new UIDClass();
        uid.Value = ThisAddIn.IDs.EmgaatsCoreExtension;
        ArcMap.Application.FindExtensionByCLSID(uid);
      }
      return _emgaatsExtension;
    }

    /// <summary>
    /// Provides a means for this extension to perform custom startup
    /// </summary>
    /// <param name="initializationData"></param>
    protected override void OnStartup()
    {
      base.OnStartup();

      _mediator = Mediator.InitializeMediator();
      _librarian = LayerLibrarian.InitializeLibrarian();

      Messenger.ReportMessage("Activated EMGAATS System", ReportableMessageType.Info);

      //SetUpDocumentEvent(ArcMap.Application.Document);           

      Mediator.ModelLoaded += Mediator_ModelLoaded;

      LayerLibrarian.CheckedIn += Librarian_CheckedIn;

      string[] args = Environment.GetCommandLineArgs();

      //if (args.Contains(SHOW_EMGAATS_FLAG))
      //ShowEmgaatsWindow();

    }   

    private void SaveUISettings()
    {
      Properties.Settings.Default.Save();
    }

    /// <summary>
    /// Sets the model's feature list data to the available model layers' data
    /// </summary>
    public void LoadModelData()
    {
      Mediator.Model.SetFeatures(ArcMap.Application, _librarian);
    }

    private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      //_emgaatsWindow.UpdateModelInfo();
    }

    private void Mediator_ModelLoaded(object sender, EventArgs e)
    {
      Mediator.Model.PropertyChanged += Model_PropertyChanged;
    }

    private void Librarian_CheckedIn(object sender, CheckInCheckOutArgs e)
    {
      if (Mediator.Model.State >= ModelState.Assembled)
      {
        Mediator.Model.SetFeature(ArcMap.Application, e.Layer);
      }
    }

    public void LoadMasterLayers()
    {
      BesAsm.Framework.ArcGisUtilities.ArcGisUtils arcUtils =
        new BesAsm.Framework.ArcGisUtilities.ArcGisUtils(ArcMap.Application);

      if (arcUtils.GetAllLayersInMap().Count > 0)
      {
        if (MessageBox.Show("Close current map and create new map with master data?"
          , "Close current map?", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning)
          != DialogResult.OK)
          return;
      }

      string templateFile = GetTemplateMxdFileName();

      ArcMap.Application.NewDocument(false, templateFile);

      return;
    }

    public static string GetTemplateMxdFileName()
    {
      string defaultFolder = Environment.GetFolderPath(Environment.SpecialFolder.Templates);
      string templateFileName = Properties.Settings.Default.DefaultWorkspace;

      string templateFile = System.IO.Path.Combine(defaultFolder, templateFileName);

      if (!System.IO.File.Exists(templateFile))
      {
        System.IO.BinaryWriter writer =
          new System.IO.BinaryWriter(System.IO.File.Open(templateFile, System.IO.FileMode.Create));
        writer.Write(Properties.Resources.EmgaatsDefaultWorkspace);
        writer.Close();
      }
      return templateFile;
    }

    #region Event Wiring for New and Open Documents
    // ArcGIS Snippet Title:
    // Add Event Wiring for New and Open Documents
    // 
    // Long Description:
    // Wire event when a document is created or opened in ArcGIS desktop applications.
    // 
    // Add the following references to the project:
    // ESRI.ArcGIS.ArcMapUI
    // ESRI.ArcGIS.Framework
    // 
    // Intended ArcGIS Products for this snippet:
    // ArcGIS Desktop (ArcEditor, ArcInfo, ArcView)
    // 
    // Applicable ArcGIS Product Versions:
    // 9.2
    // 9.3
    // 
    // Required ArcGIS Extensions:
    // (NONE)
    // 
    // Notes:
    // This snippet is intended to be inserted inside a Method.
    // It is not intended to be added to the base level of the Class.
    // 

    /// <summary>
    /// Event member variable
    /// </summary>
    /// <remarks></remarks>
    private ESRI.ArcGIS.ArcMapUI.IDocumentEvents_Event _docEvents = null;

    /// <summary>
    /// Set up the wiring of the events.
    /// </summary>
    /// <param name="myDocument"></param>
    /// <remarks></remarks>
    private void SetUpDocumentEvent(ESRI.ArcGIS.Framework.IDocument myDocument)
    {
      _docEvents = myDocument as ESRI.ArcGIS.ArcMapUI.IDocumentEvents_Event;
      _docEvents.NewDocument += new IDocumentEvents_NewDocumentEventHandler(OnNewDocument);
      _docEvents.OpenDocument += new IDocumentEvents_OpenDocumentEventHandler(OnOpenDocument);
      _docEvents.CloseDocument += new IDocumentEvents_CloseDocumentEventHandler(OnCloseDocument);
    }

    /// <summary>
    /// The NewDocument event handler. 
    /// </summary>
    /// <remarks></remarks>
    protected virtual void OnNewDocument()
    {
      ESRI.ArcGIS.Framework.IDocument theDocument = _docEvents as ESRI.ArcGIS.Framework.IDocument;
    }

    /// <summary>
    /// The OpenDocument event handler.
    /// </summary>
    /// <remarks></remarks>
    protected virtual void OnOpenDocument()
    {
      ESRI.ArcGIS.Framework.IDocument theDocument = _docEvents as ESRI.ArcGIS.Framework.IDocument;
    }

    protected virtual void OnCloseDocument()
    {
      ESRI.ArcGIS.Framework.IDocument theDocument = _docEvents as ESRI.ArcGIS.Framework.IDocument;
    }

    #endregion

    public void PromptSaveModel()
    {
      try
      {
        if (Mediator.Model.State == ModelState.Null)
          return;

        ModelSaveCommand modelSaveCommand = new ModelSaveCommand();
        modelSaveCommand.Execute();
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error saving model: " + ex.Message, "Error saving model", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }

    }

    public void PromptNewModel()
    {
      try
      {
        string modelPath;
        FolderBrowserDialogEx dlgBrowseFolder =
          PrepareFolderBrowserDialogToSelectModel("Select location of new model");
        if (dlgBrowseFolder.ShowDialog() != DialogResult.OK)
        {
          return;
        }
        modelPath = dlgBrowseFolder.SelectedPath;

        if (!PromptLoadEmgaatsLayers())
          return;

        ModelNewCommand modelNewCommand = new ModelNewCommand(modelPath);
        modelNewCommand.Execute();

        AddLastUsedModelDirectoryToSettings();

        string controlMxdPath = Mediator.Model.GetPathIfSet(ModelPathComponent.ControlMXD);
        Application.DoEvents();
        ArcMap.Application.SaveAsDocument(controlMxdPath, false);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error creating new model: " + ex.Message, "Error creating new model", MessageBoxButtons.OK, MessageBoxIcon.Error);
        Messenger.ReportMessage("Error creating new model: " + ex.Message, ReportableMessageType.Error);
        return;
      }
    }

    public void PromptOpenModel()
    {
      try
      {
        string modelPath;
        FolderBrowserDialogEx dlgBrowseFolder =
          PrepareFolderBrowserDialogToSelectModel("Select location of model to open");
        if (dlgBrowseFolder.ShowDialog() != DialogResult.OK)
        {
          return;
        }
        modelPath = dlgBrowseFolder.SelectedPath;

        OpenModel(modelPath);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error opening model: " + ex.Message, "Error opening model", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }
    }

    private bool OpenModel(string modelPath)
    {
      ModelOpenCommand modelOpenCommand = new ModelOpenCommand(modelPath);

      try
      {
        modelOpenCommand.Execute();
      }
      catch (System.IO.FileNotFoundException ex)
      {
        MessageBox.Show("No model found at " + modelPath);
        return false;
      }

      StringBuilder errorStringBuilder = new StringBuilder();
      if (Mediator.Model.Errors.Count > 0)
      {
        foreach (ReportableMessage error in Mediator.Model.Errors)
        {
          errorStringBuilder.Append(error.Description);
          errorStringBuilder.Append("\n");
        }
        MessageBox.Show(errorStringBuilder.ToString(), "Errors on open");
      }
      else
      {
        try
        {
          AddLastUsedModelDirectoryToSettings();

          ArcMap.Application.OpenDocument(
            Mediator.Model.GetPathIfSet(ModelPathComponent.ControlMXD));
        }
        catch (Exception ex)
        {
          Messenger.ReportMessage("Error loading model: " + ex.Message, ReportableMessageType.Error);
          return false;
        }
      }
      return true;
    }

    private static FolderBrowserDialogEx PrepareFolderBrowserDialogToSelectModel(string description)
    {
      FolderBrowserDialogEx dlgBrowseFolder = new FolderBrowserDialogEx
      {
        Description = description,
        ShowNewFolderButton = true,
        ShowEditBox = true,
        ShowFullPathInEditBox = true,
        NewStyle = true,
        ShowBothFilesAndFolders = false
      };
      System.Collections.Specialized.StringCollection mruModels =
        Properties.Settings.Default.MruModels;
      if (mruModels.Count > 0)
        dlgBrowseFolder.SelectedPath = mruModels[mruModels.Count - 1];
      return dlgBrowseFolder;
    }

    private void AddLastUsedModelDirectoryToSettings()
    {
      System.Collections.Specialized.StringCollection mruModelsList =
        Properties.Settings.Default.MruModels;
      mruModelsList.Add(Mediator.Model.GetPathIfSet(ModelPathComponent.RootFolder));

      int numMruModels = Properties.Settings.Default.NumMruModels;
      while (mruModelsList.Count > numMruModels)
      {
        mruModelsList.RemoveAt(0);
      }
      Properties.Settings.Default.Save();
      PrepareMruModelsList();
    }

    private bool PromptLoadEmgaatsLayers()
    {
      string templateMxdPath = GetTemplateMxdFileName();

      BesAsm.Framework.ArcGisUtilities.ArcGisUtils arcUtils =
        new BesAsm.Framework.ArcGisUtilities.ArcGisUtils(ArcMap.Application);

      string message;
      int layerCount = arcUtils.GetAllLayersInMap().Count;

      if (layerCount > 0 && !LayerLibrarian.IsMasterDataLoaded)
      {
        message = "A new ArcGIS session will be initiated using the default EMGAATS template. " +
          "The existing session will be closed. Continue?";
        if (MessageBox.Show(message, "Load new session?",
          MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK)
          return false;
      }
      else if (LayerLibrarian.IsMasterDataLoaded)
      {
        message = "All required EMGAATS data is present in the current ArcGIS session. Use existing session?";
        DialogResult result = MessageBox.Show(message, "Use existing session?",
          MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
        if (result == DialogResult.Yes)
        {
          return true;
        }
        else if (result == DialogResult.Cancel)
          return false;
      }

      ArcMap.Application.NewDocument(false, templateMxdPath);

      return true;
    }

    private void PrepareMruModelsList()
    {

      foreach (var modelPath in Properties.Settings.Default.MruModels)
      {
        //TODO: Figure out how to implment MRU list
        //if (System.IO.Directory.Exists(modelPath))
        //mruModelsListTool.ListToolItems.Insert(0, modelPath, modelPath);
      }
    }

  }
}